﻿package  
{	
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.TimerEvent;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.text.TextFieldAutoSize;
	import flash.text.AntiAliasType;
	import flash.utils.Timer;
	/**
	 * ...
	 * @author ...
	 */
	public class Slideshow extends Sprite
	{
		public static var FINISHED:String = "Finished";
		
		private var m_slides:Array;
		private var m_currentSlideInd:int = -1;
		
		private var m_slideShowTime:int = 3.0;
		private var m_slideTransitionTime:Number = 1.0;
		private var m_slideCrossoverStyle:String;
		
		private var m_showCaptions:Boolean;

		private var m_currentDisplayedSlide:Slide = null;
		private var m_nextSlide:Slide = null;
		
		private var m_nextSlideTimer:Timer;
		private var m_loadingTimer:Timer;
		
		private var m_playing:Boolean = false;
		private var m_loop:Boolean = false;
		private var m_inTransition:Boolean = false;
		
		public function get slideShowTime():int
		{
			return m_slideShowTime;
		}
		public function set slideShowTime(value:int):void
		{
			m_slideShowTime = value;
			if ( m_nextSlideTimer != null )
			{
				m_nextSlideTimer.delay = m_slideShowTime * 1000;
			}
		}
		public function get slideTransitionTime():Number
		{
			return m_slideTransitionTime;
		}
		public function set slideTransitionTime(value:Number):void
		{
			m_slideTransitionTime = value;
		}
		public function get slideCrossoverStyle():String
		{
			return m_slideCrossoverStyle;
		}
		public function set slideCrossoverStyle(value:String):void
		{
			m_slideCrossoverStyle = value;
		}
		public function get showCaptions():Boolean
		{
			return m_showCaptions;
		}
		public function set showCaptions(value:Boolean):void 
		{
			m_showCaptions = value;
			if ( m_currentDisplayedSlide != null )
			{
				if ( m_showCaptions ) m_currentDisplayedSlide.showCaption( m_slides.indexOf( m_currentDisplayedSlide )+1, m_slides.length );
				else m_currentDisplayedSlide.hideCaption();
			}
		}
		
		public function Slideshow():void
		{
			m_slides = new Array();
			m_currentSlideInd = -1;
			m_showCaptions = false;
			if (stage) init();
			else addEventListener(Event.ADDED_TO_STAGE, init);
		}
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
		}

		public function addSlide( url:String, description:String ):void
		{
			m_slides.push( new Slide( url, description ) );
		}

		public function gotoSlide( ind:int ):void
		{
			if ( ind >= m_slides.length )
			{
				throw new Error( "Attempt to go to slide out side the bounds of the existing set of slides." );
			}
			trace( "Loading slide: " + ind );
			m_currentSlideInd = ind;
			m_nextSlide = getCurrentSlide();
			this.addChild(m_nextSlide);
			
			if ( m_loadingTimer == null )
			{
				m_loadingTimer = new Timer(0.1, 0);
				m_loadingTimer.addEventListener( TimerEvent.TIMER, showLoadingMessage );
			}
			// If the slide hasn't loaded in 0.1 seconds, show the loading message.
			m_loadingTimer.start();
			m_nextSlide.addEventListener( Slide.LOADED, slideLoaded );
			m_nextSlide.addEventListener( Slide.FAILED_LOAD, slideLoadFailed );
			m_nextSlide.load();
		}

		private var m_loadingMessage:TextField;
		
		private function showLoadingMessage(evt:TimerEvent = null):void
		{
			if ( m_loadingMessage == null )
			{
				m_loadingMessage = new TextField();
				var captionFormat:TextFormat = new TextFormat( "_Font", 24, 0xFFFFFFFF, true );
				captionFormat.leftMargin = 30;
				captionFormat.rightMargin = 30;
				m_loadingMessage.defaultTextFormat = captionFormat;
				m_loadingMessage.antiAliasType = AntiAliasType.ADVANCED;
				m_loadingMessage.embedFonts = true;
				m_loadingMessage.alpha = 0.75;
				m_loadingMessage.background = true;
				//m_caption.opaqueBackground = false;
				m_loadingMessage.backgroundColor = 0xFF222222;
				m_loadingMessage.border = true;
				m_loadingMessage.borderColor = 0xFFFFFFFF;				
				
				m_loadingMessage.text = "Loading...";

				m_loadingMessage.autoSize = TextFieldAutoSize.CENTER;
				m_loadingMessage.x = (stage.stageWidth - m_loadingMessage.width) / 2;
				m_loadingMessage.y = (stage.stageHeight) / 2;
			}
			if ( !this.contains( m_loadingMessage ) )
			{
				this.addChild( m_loadingMessage );
			}
		}
		private function hideLoadingMessage():void
		{
			if ( this.contains( m_loadingMessage ) )
			{
				this.removeChild( m_loadingMessage );
			}
		}
		
		public function nextSlide(evt:TimerEvent = null):void
		{
			if ( m_inTransition ) return;
			m_inTransition = true;
			if ( m_currentSlideInd + 1 < m_slides.length )
			{
				// Not at end, go to next slide.
				gotoSlide( m_currentSlideInd + 1 );
			}
			else
			{
				// At end
				if ( m_loop || !m_playing )
				{
					// We're looping or not playing, so go back to slide 0.
					gotoSlide( 0 );
				}
				else
				{
					// At end, so signal that we're done playing to anyone interested.
					m_playing = false;
					m_inTransition = false;
					this.dispatchEvent( new Event( Slideshow.FINISHED ) );
				}
			}
		}
		
		public function prevSlide():void
		{
			if ( m_inTransition ) return;
			m_inTransition = true;
			if ( m_currentSlideInd > 0 )
			{
				// Not at end, go back one.
				gotoSlide( m_currentSlideInd - 1 );
			}
			else
			{
				// Loop around to end.
				gotoSlide( m_slides.length - 1 );
			}
		}
		
		public function play():void
		{
			// Check if we're already playing, if so, just return.
			if ( m_playing ) return;
			
			m_playing = true;
			if ( m_currentSlideInd + 1 >= m_slides.length )
			{
				// Re-start from start if we're at the end.
				m_currentSlideInd = -1;
			}
			
			// Setup the timer for the subsequent slides.
			m_nextSlideTimer = new Timer( m_slideShowTime * 1000, 1 );
			m_nextSlideTimer.addEventListener( TimerEvent.TIMER, nextSlide );

			// And advance to the next slide immediately.
			nextSlide();
		}
		
		public function pause():void
		{
			m_playing = false;
			m_nextSlideTimer.reset();
		}
		
		private function getCurrentSlide():Slide
		{
			if ( m_slides[m_currentSlideInd] != null )
			{
				return m_slides[m_currentSlideInd] as Slide;
			}
			else return null;
		}
		
		private function slideLoadFailed( evt:Event ):void 
		{
			throw new Error( "Slide load failed!!" );
		}

		private function slideLoaded( evt:Event ):void 
		{
			m_loadingTimer.reset();
			hideLoadingMessage();
			// Remove the event listeners
			m_nextSlide.removeEventListener( Slide.LOADED, slideLoaded );
			m_nextSlide.removeEventListener( Slide.FAILED_LOAD, slideLoadFailed );
			
			// Transition to this slide.
			transition();

			// We're now not downloading anything, so pre-load the next slide (if this is 
			// already loaded, .load will return immediately so no problems with calling load 
			// on slides multiple times.
			if ( m_currentSlideInd + 1 < m_slides.length )
			{
				(m_slides[m_currentSlideInd + 1] as Slide).load();
			}
		}
		
		private function transition():void
		{
			if ( m_currentDisplayedSlide != null && m_currentDisplayedSlide != m_nextSlide )
			{
				m_currentDisplayedSlide.fadeOut(m_slideTransitionTime);
			}
			if ( m_currentDisplayedSlide == m_nextSlide )
			{
				transitionComplete(null);
				return;
			}
			m_nextSlide.fadeIn(m_slideTransitionTime);
			m_nextSlide.addEventListener( Slide.FADE_IN_COMPLETE, transitionComplete );
			//m_displayedSlide = newSlide;
		}
		private function transitionComplete( event:Event ):void 
		{
			if ( m_currentDisplayedSlide != null && m_currentDisplayedSlide != m_nextSlide )
			{
				this.removeChild( m_currentDisplayedSlide );
			}
			m_currentDisplayedSlide = m_nextSlide;
			if ( m_showCaptions )
			{
				m_currentDisplayedSlide.showCaption( m_slides.indexOf( m_currentDisplayedSlide ) +1, m_slides.length );
			}
			else
			{
				m_currentDisplayedSlide.hideCaption();
			}
			m_inTransition = false;
			// If playing, start timer for next slide.
			if (m_playing)
			{
				trace( "Starting timer for next slide" );
				m_nextSlideTimer.start();
			}
		}
		
		public function resize():void
		{
			trace("Resizing...");
			if ( m_currentDisplayedSlide != null )
			{
				m_currentDisplayedSlide.resize();
			}
			if ( m_nextSlide != null )
			{
				m_nextSlide.resize();
			}
		}
	}
}